package com.test.dao;

import java.io.FileNotFoundException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.util.Log4jConfigurer;

import com.test.utils.enums.AbstractEnum;

public abstract class AbstractDao {

	private static final String LOG4J_PROPERTY_FILE = "src/main/resources/configuration/log4j.properties";

	static {
		try {
			Log4jConfigurer.initLogging(LOG4J_PROPERTY_FILE);
		} catch (FileNotFoundException fnfe) {
			fnfe.printStackTrace();
		}
	}

	protected static final Logger logger = Logger.getLogger("DAO");

	private static final String GET_INSERTED_ID =
		"select last_insert_id() from ##tn## limit 1";

	private SimpleJdbcTemplate simpleJdbcTemplate;

	public SimpleJdbcTemplate getSimpleJdbcTemplate() {
		return simpleJdbcTemplate;
	}

	public void setSimpleJdbcTemplate(SimpleJdbcTemplate simpleJdbcTemplate) {
		this.simpleJdbcTemplate = simpleJdbcTemplate;
	}

	/**
	 * 
	 * @param propertyName
	 * @return
	 */
	protected abstract String mapPropertyNameToDBColumn(final String propertyName);

	/**
	 * 
	 * @param affectedRows
	 * @return
	 */
	protected DBOperationResult getResultForAffectedRows(final int affectedRows) {
		if (affectedRows <= 0) {
			return DBOperationResult.ERROR_NOT_FOUND;
		} else if (affectedRows == 1) {
			return DBOperationResult.SUCCESS_SINGLE_LINE_OPERATION;
		} else {
			return DBOperationResult.SUCCESS_MULTIPLE_LINES_OPERATION;
		}
	}

	/**
	 * 
	 * @param dae
	 * @return
	 */
	protected DBOperationResult getResultForDataAccessException(final DataAccessException dae) {
		return DBOperationResult.ERROR_UNKNOWN;
	}

	/**
	 * 
	 * @param tableName
	 * @return
	 * @throws DataAccessException
	 */
	protected String getInsertedId(final String tableName) throws DataAccessException {
		logger.debug("Retrieving auto generated ID from table " + tableName);
		final String id = getSimpleJdbcTemplate().queryForObject(GET_INSERTED_ID.replace("##tn##", tableName), String.class);
		logger.debug("Retrieved ID " + id + " from table " + tableName);
		return id;
	}

	/**
	 * 
	 * @param propertyCriteriaMap
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	protected String buildWhereCondition(final Map<String, Collection<? extends Object>> propertyCriteriaMap) {
		final StringBuilder sb = new StringBuilder("");
		if (propertyCriteriaMap == null) {
			return sb.toString();
		}
		for (Map.Entry<String, Collection<? extends Object>> entry : propertyCriteriaMap.entrySet()) {
			if (StringUtils.isEmpty(entry.getKey()) || entry.getValue() == null || entry.getValue().size() == 0) {
				continue;
			}
			if (sb.length() > 0) {
				sb.append(" and ");
			}
			sb.append(mapPropertyNameToDBColumn(entry.getKey())).append(" in (");
			final StringBuilder values = new StringBuilder("");
			final boolean isCharacterValue = isCharacterValue(entry.getValue().iterator().next());
			final Iterator<? extends Object> it = entry.getValue().iterator();
			while (it.hasNext()) {
				final Object o = it.next();
				if (o == null) {
					continue;
				}
				if (values.length() > 0) {
					values.append(",");
				}
				if (isCharacterValue) {
					values.append("'");
				}
				if (o instanceof AbstractEnum) {
					values.append(((AbstractEnum) o).getValue().toString());
				} else {
					values.append(o.toString());
				}
				if (isCharacterValue) {
					values.append("'");
				}
			}
			sb.append(values);
			sb.append(")");
		}
		return sb.toString();
	}

	private boolean isCharacterValue(final Object value) {
		if (value instanceof Integer || value instanceof Long || value instanceof Short || value instanceof Boolean || value instanceof Double) {
			return false;
		} else if (value instanceof AbstractEnum){
			@SuppressWarnings("rawtypes")
			final Object val = ((AbstractEnum) value).getValue();
			if (val instanceof Integer || val instanceof Long || val instanceof Short) {
				return false;
			}
		}
		return true;
	}
}
